////////////////////////////////////////////////////////////////
// filename: game.cpp
// description: source file for our game engine
// autor: Jay Tomten (jaytomten@gmail.com)
// notes:

// namespace

// defines

// includes
#if defined(__APPLE__)
#include <OpenGL/gl.h>
#include <OpenGL/glu.h>
#else
#include <GL/gl.h>
#include <GL/glu.h>
#endif
#include <SDL/SDL.h>
#include <iostream>
#include <fstream>
#include "world.h"
#include "character.h"
#include "particle.h"
#include "game.h"

// globals

////////////////////////////////////////////////////////////////
// constructor
Game::Game()
    {

    // use Init()

    }

////////////////////////////////////////////////////////////////
// destructor
Game::~Game()
    {

    delete screen;
    delete gList;
    delete gScores;

    }

////////////////////////////////////////////////////////////////
// Init() - initialize the game
bool Game::Init()
    {

    bool alive = true;

    // set default screen settings then try and read the cfg file
    gScreen.width = 1024;
    gScreen.height = 768;
    gScreen.bpp = 24;
    gScreen.refresh = 50;
    gScreen.fullscreen = 0;
    gScreen.maxFPS = 60.0;
    gScreen.smooth = 1;
    soundFXVolume = 30;
    musicVolume = 100;
    gScores = new Scores[10];
    for(int i = 0; i < 10; i++)
        {
        gScores[i].name = "AAA";
        gScores[i].score = 100;
        }
    ReadConfig();
    if(gScreen.height < 384)
        {
        cout << "The screen must have a height of at least 384 pixels.";
        gScreen.width = 1024;
        gScreen.height = 768;
        }

    // set up the screen
    cout << "Screen initialization... ";
    if(SDL_Init(SDL_INIT_EVERYTHING) == -1)
        {
        cout << "Unable to initialize SDL: ", SDL_GetError();
        alive = false;
        return alive;
        }
    else cout << "SDL init success ";
    if(gScreen.smooth == 1)
        {
        if(SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8) != 0) cout << "Failed to set RED SIZE.\n";
        if(SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8) != 0) cout << "Failed to set BLUE SIZE.\n";
        if(SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE,8) != 0) cout << "Failed to set GREEN SIZE.\n";
        if(SDL_GL_SetAttribute(SDL_GL_BUFFER_SIZE, 32) != 0) cout << "Failed to set BUFFER SIZE.\n";
        if(SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1) != 0) cout << "Failed to set DOUBLE BUFFER.\n";
        }
    else
        {
        if(SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8) != 0) cout << "Failed to set RED SIZE.\n";
        if(SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8) != 0) cout << "Failed to set BLUE SIZE.\n";
        if(SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8) != 0) cout << "Failed to set GREEN SIZE.\n";
        if(SDL_GL_SetAttribute(SDL_GL_BUFFER_SIZE, 16) != 0) cout << "Failed to set BUFFER SIZE.\n";
        if(SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1) != 0) cout << "Failed to set DOUBLE BUFFER.\n";
        }
    if(gScreen.fullscreen)
        {
#if defined(__WIN32__)
        screen = SDL_SetVideoMode(gScreen.width, gScreen.height, 0, SDL_OPENGL | SDL_HWSURFACE | SDL_FULLSCREEN);
#else
        screen = SDL_SetVideoMode(gScreen.width, gScreen.height, 0, SDL_OPENGL | SDL_HWSURFACE);
        SDL_WM_ToggleFullScreen(screen);                        // linux only... use SDL_FULLSCREEN flag in windows
#endif
        }
    else screen = SDL_SetVideoMode(gScreen.width, gScreen.height, 0, SDL_OPENGL | SDL_HWSURFACE);
    if (screen == NULL)
        {
        cout << "failed: Unable to set video mode: " << SDL_GetError() << ".\n";
        alive = false;
        }
    else cout << "success.\n";
    SDL_WM_SetCaption("BMTG3","BMTG3");

    // basic screen/viewport stuff
    glViewport(0, 0, gScreen.width, gScreen.height);
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    glClearDepth(1.0);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(45.0f, ((float)gScreen.width / 2.0f) / (float)gScreen.height, 0.01f, 500.0f);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    // shading depth and smoothing (the nice stuff)
    // this is the easy way of doing it, I can get better results if picking these at the source...
    glEnable(GL_ALPHA_TEST);
    glAlphaFunc(GL_GREATER, 0.01);
    if(gScreen.smooth > 0)
        {
        glEnable(GL_LINE_SMOOTH);
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        }
    if(gScreen.smooth > 1)
        {
        glEnable(GL_CULL_FACE);
        glEnable(GL_POINT_SMOOTH);
        glEnable(GL_DEPTH_TEST);
        glDepthFunc(GL_LEQUAL);
        }

    // lights
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glEnable(GL_COLOR_MATERIAL);
    glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
    glShadeModel(GL_SMOOTH);
    float lightAmb[] = {5.0f, 5.0f, 5.0f, 1.0f};
    float lightDif[] = {0.0f, 0.0f, 1.0f, 1.0f};
    float lightMat[] = {0.5f, 0.5f, 0.5f, 0.5f};
    float lightPos[] = {10.0f, 10.0f, 0.0f, 0.0f};
    glLightfv(GL_LIGHT0, GL_DIFFUSE, lightDif);
    glLightfv(GL_LIGHT0, GL_AMBIENT, lightAmb);
    glLightfv(GL_LIGHT0, GL_POSITION, lightPos);
    glMaterialfv(GL_FRONT, GL_SPECULAR, lightMat);

    // the much easier stuff
    gList = new List();
    gInput.Init(INPUT_1P);
    gSound.Init(musicVolume, soundFXVolume);
    reload = false;
    stateTimer = 0.0f;

    // return success!
    if(alive) cout << "Engine initialization... success.\n";
    else cout << "Engine initialization... failed.\n";
    return alive;

    }

////////////////////////////////////////////////////////////////
// Exit() - exit function to end nicely
void Game::Exit()
    {

    WriteConfig();                                              // save our last game configuration
#if defined(__WIN32__)
    // nothing
#else
    if(gScreen.fullscreen == 1) SDL_WM_ToggleFullScreen(screen); // linux only
#endif
    cout << "Engine shutdown success!\n";
    exit(0);

    }

////////////////////////////////////////////////////////////////
// Update() - update the game objects
bool Game::Update(float d)
    {

    // pretty simple update, level, time, and input
    bool alive = true;
    gTime.LockFPS(gScreen.maxFPS);                              // hold to a decent framerate (wait a bit here)
    alive = gTime.Update(d);                                    // update our time, passing old time
    if  (alive)
        {
        if  (state != GAME_MULTI_PLAYER_LOCAL)
            {
            alive = gLevel.Update(d);                           // update our level
            }
        alive = gInput.Update(d);                               // update our input class
        }

    Node *curr;
    Node *dead;
    curr = gList->front;
    while (curr != NULL)                                        // update the game objects
        {
        if(!curr->o->Update(d))                                 // if Draw() returns false (for a dead object) remove it
            {
            dead = curr;
            gList->Remove(dead);
            }
        else if (curr->o->state != STATE_BUILDING)
            {
            curr->o->state = STATE_NORM;                        // reset for collision dection when not building
            }
        curr = curr->next;
        }

    return alive;
    }

////////////////////////////////////////////////////////////////
// Draw() - draw all the objects to the screen, delete the dead ones
void Game::Draw()
    {
    Node *curr;
    curr = gList->front;
    while (curr != NULL)
        {
        curr->o->Draw();
        curr = curr->next;
        }
    delete curr;
    }

////////////////////////////////////////////////////////////////
// ReadConfig() - read our configuration file
void Game::ReadConfig()
    {
    ifstream ifs("../conf/game.cfg");                           // open config file
    if  (ifs)
        {
        while (!ifs.eof())                                      // do while we have not reached the end of the file
            {
            char buffer[128];
            ifs >> buffer;                                      // read a string at a time
            if(string("screen_width") == buffer) ifs >> gScreen.width; // check what we found and store
            else if(string("screen_height") == buffer) ifs >> gScreen.height;
            else if(string("screen_bpp") == buffer) ifs >> gScreen.bpp;
            else if(string("screen_refresh") == buffer) ifs >> gScreen.refresh;
            else if(string("screen_fullscreen") == buffer) ifs >> gScreen.fullscreen;
            else if(string("screen_maxfps") == buffer) ifs >> gScreen.maxFPS;
            else if(string("screen_smooth") == buffer) ifs >> gScreen.smooth;
            else if(string("soundfx_volume") == buffer) ifs >> soundFXVolume;
            else if(string("music_volume") == buffer) ifs >> musicVolume;
            }
        }
    else
        {
        cout <<"Unable to load config file.\n";
        }
    ifs.close();                                                // close score file

    ifstream ifs2("../conf/scores.cfg");                        // open config file
    if  (ifs2)
        {
        int i = 0;
        while(!ifs2.eof() && i < 10)                            // do while we have not reached the end of the file
            {
            //char buffer[128];
            //ifs >> buffer;                                    // read a string at a time
            ifs2 >> gScores[i].name;                            // check what we found and store
            ifs2 >> gScores[i].score;
            i++;
            }
        }
    else
        {
        cout <<"Unable to load score file.\n";
        }
    ifs2.close();                                               // close score file

    }

////////////////////////////////////////////////////////////////
// WriteConfig() - write our configuration file
void Game::WriteConfig()
    {

    ofstream ofs("../conf/game.cfg");                           // open the config file ".cfg" for writing
    if(ofs)                                                     // if opened OK write the file
        {
        ofs << "screen_width " << gScreen.width << "\n";
        ofs << "screen_height " << gScreen.height << "\n";
        ofs << "screen_bpp " << gScreen.bpp << "\n";
        ofs << "screen_refresh " << gScreen.refresh << "\n";
        ofs << "screen_fullscreen " << gScreen.fullscreen << "\n";
        ofs << "screen_maxfps " << gScreen.maxFPS << "\n";
        ofs << "screen_smooth " << gScreen.smooth << "\n";
        ofs << "soundfx_volume " << soundFXVolume <<"\n";
        ofs << "music_volume " << musicVolume << "\n";
        }
    else
        {
        cout <<"Unable to write config file, check permissions.\n";
        }
    ofs.close();

    ofstream ofs2("../conf/scores.cfg");                        // open the config file ".cfg" for writing
    int i = 0;
    if  (ofs2)                                                  // if opened OK write the file
        {
        while(i < 10)                                           // do while we have not reached the end of the file
            {
            ofs2 << gScores[i].name << " ";                     // check what we found and store
            ofs2 << gScores[i].score << endl;
            i++;
            }
        }
    else
        {
        cout <<"Unable to write score file, check permissions.\n";
        }
    ofs2.close();

    }


